package com.xuecheng.search.service;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xuecheng.framework.domain.search.CourseBaseSearch;
import com.xuecheng.framework.domain.search.CourseSearchParam;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.QueryResponseResult;
import com.xuecheng.framework.model.response.QueryResult;
import com.xuecheng.search.search.CourseBaseSearchRepository;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.*;


@Service
public class EsCourseService {

    @Resource
    CourseBaseSearchRepository courseBaseSearchRepository;

    @Resource
    ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 分页搜索数据
     *
     * @param page 页码
     * @param size 每页显示多少个
     * @return 结果
     * @pram courseSearchParam 搜索参数
     */
    public QueryResponseResult<CourseBaseSearch> list(int page, int size, CourseSearchParam courseSearchParam) {

        // 构建布尔查询,查询多个索引
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 添加keyword关键字
        if (StringUtils.isNotEmpty(courseSearchParam.getKeyword())) {
            // 构建multiMatchQueryBuilder 多个索引查询 设置查询字段和权重
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(courseSearchParam.getKeyword(), "name", "description", "teachplan")
                    // 搜索占比百分之70
                    .minimumShouldMatch("70%")
                    // 提高name的权重
                    .field("name", 10);
            // 将构造好的多个索引查询存入布尔查询
            boolQueryBuilder.must(multiMatchQueryBuilder);
        }
        //添加一级分类
        if (StringUtils.isNotEmpty(courseSearchParam.getMt())) {
            //termQuery不分词查询
            boolQueryBuilder.filter(QueryBuilders.termQuery("mt", courseSearchParam.getMt()));
        }
        //添加二级分类
        if (StringUtils.isNotEmpty(courseSearchParam.getSt())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("st", courseSearchParam.getSt()));
        }
        // 按照难度等级
        if (StringUtils.isNotEmpty(courseSearchParam.getGrade())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("grade", courseSearchParam.getGrade()));
        }
        // 分页
        Pageable pageable = PageRequest.of(page - 1, size);
        // 构建最后的查询
        SearchQuery build = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withPageable(pageable)
                // 指定高亮字段和标签
                .withHighlightFields(new HighlightBuilder.Field("name").preTags("<font class='highlight'>").postTags("</font>")
                ).build();
        // 提交得到响应
//        Page<CourseBaseSearch> search = courseBaseSearchRepository.search(build);  非高亮
        // 提交查询 高亮显示某一个或者多个字段
        AggregatedPage<CourseBaseSearch> aggregatedPage = elasticsearchTemplate.queryForPage(build, CourseBaseSearch.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                long totalHits = response.getHits().totalHits();
                List<CourseBaseSearch> results = new ArrayList<>();
                for (SearchHit searchHit : response.getHits()) {
                    // 取出查询出来的一条原始数据进行组装
                    Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
                    //封装其他属性
                    CourseBaseSearch courseBaseSearch = JSONObject.parseObject(JSONObject.toJSONString(sourceAsMap), CourseBaseSearch.class);
                    // 判断是否传递了关键字,传递了就进行高亮
                    if (StringUtils.isNotEmpty(courseSearchParam.getKeyword())) {
                        if (!(response.getHits().getHits().length <= 0)) {
                            //封装高亮的name, 可以直接封装多个
                            HighlightField highlight = searchHit.getHighlightFields().get("name");
                            if (highlight != null) {
                                courseBaseSearch.setName(highlight.fragments()[0].toString());
                            }
                        }
                    }
                    results.add(courseBaseSearch);
                }
                return new AggregatedPageImpl<T>((List<T>) results, pageable, totalHits, response.getAggregations(), response.getScrollId());
            }
        });
        long totalElements = aggregatedPage.getTotalElements();
        QueryResult<CourseBaseSearch> queryResult = new QueryResult<>();
        queryResult.setTotal(totalElements);
        queryResult.setList(aggregatedPage.getContent());

        return new QueryResponseResult<>(CommonCode.SUCCESS, queryResult);
    }

    /**
     * 根据课程id查询一个数据
     *
     * @param courseId 课程id
     * @return 查询的结果
     */
    public QueryResponseResult<CourseBaseSearch> findByCourseId(String courseId) {
        Optional<CourseBaseSearch> optional = courseBaseSearchRepository.findById(courseId);
        if (optional.isPresent()){
            List<CourseBaseSearch> list = new ArrayList<>();
            list.add(optional.get());
            QueryResult<CourseBaseSearch> queryResult = new QueryResult<>();
            queryResult.setList(list);
            return new QueryResponseResult<>(CommonCode.SUCCESS, queryResult);
        }else {
            return new QueryResponseResult<>(CommonCode.FAIL, null);
        }
    }
}
